#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <resolv.h>
#include <arpa/nameser.h>
#include <sys/mman.h>
#include <errno.h>
#include "debug.h"

#include "clinet.h"

// domain --> ip
// return the gethostbyname ok host number.
// notice: ip addr may not be one-one mapping.
int gethostbyname_proc(const char *name[], char *ip[], int num)
{
    if(name == NULL || ip == NULL || num <= 0)
    {
        DBG(ERR, "invalid params!\n");
        return -1;
    }

    int i = 0, ok_cnt = 0;
    struct hostent *host;

    for(i = 0; i < num && name[i]; i++)
    {
        // get host ip
        res_init(); /* clear dns_cache */
        host = gethostbyname(name[i]);

        if(host == NULL)
        {
            // use h_errno not errno variable
            DBG(ERR, "get host %s err:%s!\n", name[i], hstrerror(h_errno));
        }
        else
        {
            // only get the first ipv4 addr if host has many ipv4 addrs
            strcpy(ip[ok_cnt++], inet_ntoa(*((struct in_addr *)host->h_addr)));
            DBG(LOG, "gethostbyname %s success,ip:%s!\n", name[i], ip[ok_cnt - 1]);
        }
    }

    return ok_cnt;
}


// create socket
int connect_tcp_server(const char *ip, unsigned short port, int timeout)
{
    int ret;
    int cli_sock = 0;
    struct sockaddr_in serv_addr;
    socklen_t serv_addr_len;

    if(ip == NULL)
    {
        DBG(ERR, "invalid params!\n");
        return -1;
    }

    cli_sock = socket(AF_INET, SOCK_STREAM, 0);

    if(cli_sock < 0)
    {
        DBG(ERR, "create socket failed:%s\n", strerror(errno));
        return -1;
    }

    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;

    if(inet_aton(ip, &serv_addr.sin_addr) == 0)
    {
        DBG(ERR, "server IP Address Error:%s\n", strerror(errno));
        close(cli_sock);
        return -1;
    }

    serv_addr.sin_port = htons(port);
    serv_addr_len = sizeof(serv_addr);

    // set timeout
    if(timeout > 0)
    {
        struct timeval to;
        to.tv_sec = timeout;
        to.tv_usec = 0;
        //setsockopt(cli_sock,SOL_SOCKET,SO_SNDTIMEO,&to);
        setsockopt(cli_sock, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to));
    }

    ret = connect(cli_sock, (struct sockaddr *)&serv_addr, serv_addr_len);

    if(ret < 0)
    {
        DBG(ERR, "connect to %s:%hu err:%s!\n", ip, port, strerror(errno));
        close(cli_sock);
        return -1;
    }
    else
    {
        DBG(LOG, "connect %s:%hu success and cli_sock:%d!\n", ip, port, cli_sock);
        return cli_sock;
    }
}

int systemUs(char *cmdString, int timeOut)
{
    pid_t pid;
    int status = 0, ret, cnt = 0;

    if(cmdString == NULL || timeOut == 0)
    {
        DBG(ERR, "invalid parameters.\n");
        return -1;
    }

    pid = vfork();

    if(pid == 0)
    {
        execl("/bin/sh", "sh", "-c", cmdString, (char *)0);
        _exit(127);
    }
    else
    {
        const int WAIT_TIME = 50000;
        int left = (timeOut * 1000) / (WAIT_TIME / 1000);

        while((ret = waitpid(pid, &status, WNOHANG)) <= 0)
        {
            /*err*/
            if(ret == -1 && errno != EINTR)
            {
                status = -1;
                DBG(ERR, "execl %s.\n", cmdString);
                break;
            }

            cnt++;
            usleep(WAIT_TIME);	//1ms

            if(cnt >= left)
            {
                kill(pid, 9);
                status = -1;
                break;
            }
        }
    }

    return status;
}

/*2^3  n = 2; m = 3*/
static int power2_m(int n, int m)
{
    int i, des;

    if(m == 0)
        return 1;

    des = 1;

    for(i = 0; i < m; i++)
        des *= n;

    return des;
}

int atoi_m(unsigned char *src)
{
    int len, i, des;
    unsigned char ops;
    unsigned char tmp[1024];
    des = 0;
    len = strlen(src);

    for(i = 0; i < len; i++)
        tmp[i] = src[len - i - 1];

    for(i = 0; i < len; i++)
    {
        ops = tmp[i];

        if(ops >= 48 && ops <= 57)	//num
            des += (ops - 48) * power2_m(2, i * 4);
        else if(ops >= 65 && ops <= 70)		//A~F
            des += (ops - 55) * power2_m(2, i * 4);
        else if(ops >= 97 && ops <= 102)		//a~f)
            des += (ops - 87) * power2_m(2, i * 4);
        else
            return 0;
    }

    return des;
}

/* fifo create , read, and write infterface */
int fifo_creat(char *name)
{
    int ret;
    ret = mkfifo(name, O_RDWR | O_CREAT | 0666);

    if(ret)
    {
        DBG(ERR, "mkfifo: %s err!\n", name);
        return -1;
    }

    return 0;
}

int readfifo(int fd, char *buf, int size)
{
    int ret, sum = 0;
    int count = 0;

    do
    {
        buf = buf + sum;
        ret = read(fd, buf, size - sum);

        if(ret > 0)
            sum += ret;

        if(sum != size)
        {
            usleep(100);
        }

        count ++;

        if((count > 20) && (ret < 1))//阻塞写
        {
            DBG(ERR, "read fifo time out.\n");
            return sum;
        }
    }
    while(sum < size);

    return sum;
}


int writefifo(int fd, char *buf, int size)
{
    int ret, sum = 0;
    int count = 0;

    do
    {
        buf = buf + sum;
        ret = write(fd, buf, size - sum);

        if(ret > 0)
            sum += ret;

        if(sum != size)
        {
            usleep(100);
        }

        count ++;

        if((count > 20) && (ret < 1))//非阻塞写
        {
            return sum;
        }
    }
    while(sum < size);

    return sum;
}

